import { t } from "i18next"
import { fire } from "./bullet"
import { Message } from "@shares/message/message"
import { gunData } from './gun_data'
import { per_elapsedTimeMS } from "../延迟计时器"
import { history } from "./数据库"

type Player_save_data = {
    money:number,
    bag:number[]
}
const stor = new history<Player_save_data>('player_data',(key) => {
    return {money:0,bag:[0]}
},true)

type PlayerData = {
    name:string,
    position:GameVector3,
    last_pos:GameVector3,
    velocity:GameVector3,

    hp:number,

    mp:number,

    io:[{
        value:number,
        name:string
    }][]

    entitys:{
        player:GamePlayerEntity,

        body:GameEntity

        bound:GameEntity

        gun:GameEntity

        crama:GameEntity
    }

    gun:{
        gunId:number,
        Bullet:number
        can_fire:boolean
        wait_time:number
    }

    sport:'Run'|'None',
    can_jump:boolean,
}

const entity_add_pos = {
    body:{
        body:new GameVector3(0,0,0),

        head:new GameVector3(0,0.56,0),

        left_hand:new GameVector3(-0.3,0,0),
        right_hand:new GameVector3(0.3,0,0),

        left_foot:new GameVector3(-0.1,-0.72,0),
        right_foot:new GameVector3(0.1,-0.72,0),

        crama:new GameVector3(0,0.6,0)
    },
    gun:new GameVector3(0,0,0)
}


const rpos = new GameVector3(130,10,130)
const max_hp = 100,max_mp = 100

const players:Player[] = []
const save_UserID:{userId:string,player:Player}[] = []

export class Player
{
    data:PlayerData

    constructor(entity:GamePlayerEntity)
    {
        //stor
        (async () => {
            let data = await stor.get(entity.player.userId)
            if(data)
            {
                this.money = data.money
                this.bag = data.bag
            }
            save_UserID.push({userId:entity.player.userId,player:this})
            entity.player.onChat(({message}) => {
                if(message.startsWith('/'))
                {
                    const cmd = message.slice(1)
                    if(cmd == 'save')
                    {
                        stor.set(entity.player.userId,{money:this.money,bag:this.bag})
                    }
                }
            })
        })()
        
        //

        const gun_Id = 0
        //方向为+Z
        this.data = {
            sport:'None',
            can_jump:false,
            name:entity.player.name,
            position:rpos,
            last_pos:rpos,
            velocity:new GameVector3(0,0,0),
        
            hp:max_hp,
            mp:max_mp,

            io:[],

            entitys:{
                player:entity,

                body:world.createEntity({
                    id:'player',
                    mesh:'mesh/兵.vb',
                    meshScale:new GameVector3(0.9/16,0.9/16,0.9/16),

                    collides:false,
                    fixed:false,
                    gravity:false,

                    position:rpos.add(entity_add_pos.body.body),
                    tags:[entity.player.name]
                })!,
                    
                
                bound:world.createEntity({
                    id:'player',
                    mesh:'mesh/单位像素.vb',
                    meshScale:new GameVector3(0.6,1.8,0.6),
                    meshColor:new GameRGBAColor(0,0,0,0.3),

                    collides:true,
                    fixed:false,
                    gravity:true,

                    position:rpos,
                    tags:[entity.player.name]
                })!,

                gun:world.createEntity({
                    id:'player',
                    mesh:gunData[gun_Id].mesh,
                    meshScale:new GameVector3(1/16,1/16,1/16),

                    collides:false,
                    fixed:false,
                    gravity:false,

                    position:rpos,
                    tags:[entity.player.name]
                })!,

                crama:world.createEntity({
                    id:'player',
                    mesh:'mesh/单位像素.vb',
                    meshScale:new GameVector3(0.01,0.01,0.01),
                    meshColor:new GameRGBAColor(0,0,0,0),

                    collides:false,
                    fixed:false,
                    gravity:false,

                    position:rpos.add(entity_add_pos.body.crama),
                    tags:[entity.player.name]
                })!,
            },

            gun:{
                gunId:0,
                Bullet:gunData[gun_Id].bullets,
                can_fire:true,
                wait_time:0
            }
        }

        //look
        entity.player.cameraEntity = this.data.entitys.crama
        entity.player.cameraMode = GameCameraMode.FPS
        //entity.player.cameraFovY = 0.3
        entity.position.set(128.5,2.5,128.5)

        //jump

        this.data.entitys.bound.onVoxelSeparate(({x,y,z}) => {
            if(y <= this.data.position.y-this.data.entitys.bound.bounds.y-0.5 && voxels.getVoxel(x,y+1,z) == 0)
            {
                this.data.can_jump = true
            }
            else if(this.data.can_jump && y <= this.data.position.y-this.data.entitys.bound.bounds.y+1 && voxels.getVoxel(x,y+1,z) == 0)
            {
                this.jump()
            }
        })
        //jump
        entity.player.onPress(({button}) => {
            if(button == GameButtonType.JUMP && this.data.can_jump)
            {
                this.jump()
            }
            else if(button == GameButtonType.ACTION0)
            {
                this.fire()
            }
            else if(button == GameButtonType.ACTION1 && entity.player.crouchButton)
            {
                //bag
                this.openbag()
            }
        })
        //R
        entity.player.onKeyDown(async ({keyCode}) => {
            if(keyCode == 82)
            {
                this.r_gun()
            }
        })
        //add player to players
        players.push(this)
        //gun
        remoteChannel.sendClientEvent<Message<'gun'>>(this.data.entitys.player,{
            type:'gun',
            data:{
                text:`${this.data.gun.Bullet}/${gunData[this.data.gun.gunId].bullets}`,
                image:gunData[this.data.gun.gunId].image
            }
        })
        return this
    }

    destory()
    {
        this.data.entitys.gun.destroy()
        this.data.entitys.body.destroy()
        this.data.entitys.bound.destroy()
        this.data.entitys.crama.destroy()
        players.splice(players.findIndex(e => e.data.name == this.data.name),1)
    }

    jump()
    {
        if(this.data.entitys.bound.fluidContacts.length == 0)
            this.data.can_jump = false
        this.data.entitys.bound.velocity.y = 0.6
        this.data.entitys.bound.sound('audio/jump.mp3')
    }

    async fire()
    {

        const maxlen = gunData[this.data.gun.gunId].len

        if(!this.data.gun.can_fire || this.data.hp <= 0)
        {    
            return
        }
        this.data.gun.can_fire = false
        do
        {
            this.data.entitys.gun.sound('audio/gunshot.mp3')
            //fire
            const num = gunData[this.data.gun.gunId].type == '霰弹枪'?Math.floor(Math.random()*5+5):1
            const mir = gunData[this.data.gun.gunId].type == '霰弹枪'?4:gunData[this.data.gun.gunId].len/2

            const cos_pitch = Math.cos(this.data.entitys.player.player.cameraPitch)
            const dirs = new GameVector3(
                -Math.cos(this.data.entitys.player.player.cameraYaw)*cos_pitch,
                -Math.sin(this.data.entitys.player.player.cameraPitch),
                -Math.sin(this.data.entitys.player.player.cameraYaw)*cos_pitch
            ).scale(mir)

            for(let i = 0;i < num;i++)
            {
                const dir = dirs.add(new GameVector3(Math.random()-0.5,Math.random()-0.5,Math.random()-0.5)).scale(1/mir)

                const raycast = world.raycast(this.data.position.add(new GameVector3(0,0.38,0)),dir,{
                    ignoreSelector:`.${this.data.entitys.player.player.name}`,
                    maxDistance:maxlen
                })
                console.log('len',raycast.distance)
                fire(
                    raycast.origin,
                    dir,
                    5,
                    raycast.distance/5*per_elapsedTimeMS
                )

                setTimeout(() => {
                    // const raycast = world.raycast(this.data.position.add(new GameVector3(0,0.38,0)),dir,{
                    //     ignoreSelector:`.${this.data.entitys.player.player.name}`,
                    //     maxDistance:maxlen
                    // })
                    if(raycast.hitEntity && raycast.hitEntity.id != 'player')
                    {
                        let xs = 1
                        if(raycast.hitPosition.y > raycast.hitEntity.position.y+raycast.hitEntity.bounds.y/4)
                            xs = 2.5
                        //
                        raycast.hitEntity.hurt(gunData[this.data.gun.gunId].hurt*xs)
                        if(raycast.hitEntity.hp <= 0)
                        {
                            const add_exp = Math.floor(Math.random()*10+10)
                            this.data.entitys.player.player.directMessage(`你击杀了怪物，获得了${add_exp}$！`)
                            this.money += add_exp
                        }
                        console.log('hurt')
                    }
                },raycast.distance/20*per_elapsedTimeMS)
            }

            //
            this.data.gun.Bullet--

            remoteChannel.sendClientEvent<Message<'gun'>>(this.data.entitys.player,{
                type:'gun',
                data:{
                    text:`${this.data.gun.Bullet}/${gunData[this.data.gun.gunId].bullets}`,
                    image:gunData[this.data.gun.gunId].image
                }
            })

            this.data.gun.wait_time = Math.min(gunData[this.data.gun.gunId].max_wait_time,this.data.gun.wait_time+gunData[this.data.gun.gunId].up_angle)

            await sleep(gunData[this.data.gun.gunId].wait_time*100)
        }
        while(this.data.gun.Bullet > 0 && gunData[this.data.gun.gunId].type == '连发' && this.data.entitys.player.player.action0Button)

        if(this.data.gun.Bullet <= 0)
        {
            await this.r_gun()
        }
        this.data.gun.can_fire = true    
    }

    hurt(damage:number,type:string)
    {
        this.data.hp-=damage

        this.data.entitys.bound.hp = Math.max(0,this.data.hp)
        this.data.entitys.bound.sound('audio/hurt.mp3')
        this.data.entitys.bound.enableDamage = true

        remoteChannel.sendClientEvent<Message<'HP'>>(this.data.entitys.player,{
            type:'HP',
            data:Math.max(0,this.data.hp)
        })

        if(this.data.hp <= 0)
        {
            this.die()
        }
        else
        {
            this.data.entitys.player.player.directMessage(`[HP：${this.data.hp}/${max_hp}]你受到了${damage}点伤害！`)
        }
    }

    async die()
    {
        world.say(`玩家[${this.data.name}]阵亡！`)

        //entity
        this.data.entitys.bound.position.y = -10
        this.data.entitys.bound.velocity.set(0,0,0)
        this.data.entitys.gun.position.y = -10
        this.data.entitys.gun.velocity.set(0,0,0)
        this.data.entitys.body.position.y = -10
        this.data.entitys.body.velocity.set(0,0,0)

        this.data.entitys.crama.position = new GameVector3(128,64,128)
        this.data.entitys.crama.velocity.set(0,0,0)

        this.data.entitys.bound.gravity = false
        //
        
        //

        await sleep(1000)
        for(let i = 0;i < 5;i++)
        {
            this.data.entitys.player.player.directMessage(`${5-i}秒后复活……`)
            await sleep(1000)
        }
        this.data.hp = 100
        this.data.entitys.bound.hp = 100
        this.data.entitys.bound.position = rpos.clone()
        this.data.entitys.bound.gravity = true
        this.data.gun = {
            gunId:this.data.gun.gunId,
            Bullet:gunData[this.data.gun.gunId].bullets,
            can_fire:true,
            wait_time:0
        }

        remoteChannel.sendClientEvent<Message<'HP'>>(this.data.entitys.player,{
            type:'HP',
            data:this.data.hp
        })

        remoteChannel.sendClientEvent<Message<'gun'>>(this.data.entitys.player,{
            type:'gun',
            data:{
                text:`${this.data.gun.Bullet}/${gunData[this.data.gun.gunId].bullets}`,
                image:gunData[this.data.gun.gunId].image
            }
        })
    }

    /**
     * 接口：右键事件
     */
    onPree1(thing:(event:GameInputEvent) => void)
    {
        this.data.entitys.player.player.onPress((event) => {
            if(event.button == GameButtonType.ACTION1)
            {
                thing(event)
            }
        })
    }

    /**
     * 背包
     */
    bag:number[] = [0]
    money:number = 0
    async openbag()
    {
        const entity = this.data.entitys.player

        const list:string[] = []
        for(const i of this.bag)
            list.push(gunData[i].name)
        list.push('...')

        const xz = await entity.player.dialog<GameSelectDialogParams>({
            type:GameDialogType.SELECT,
            title:'背包',
            content:'你有以下物品：',
            options:list
        })
        if(xz)
        {
            if(xz.index == list.length-1)
            {
                this.other()
            }
            else
            {
                this.data.gun.gunId = this.bag[xz.index]
                this.data.entitys.gun.mesh = gunData[this.data.gun.gunId].mesh

                remoteChannel.sendClientEvent<Message<'gun'>>(this.data.entitys.player,{
                    type:'gun',
                    data:{
                        text:`${this.data.gun.Bullet}/${gunData[this.data.gun.gunId].bullets}`,
                        image:gunData[this.data.gun.gunId].image
                    }
                })

                this.r_gun()
            }
        }
    }
    async other()
    {
        const entity = this.data.entitys.player

        const xz = await entity.player.dialog<GameSelectDialogParams>({
            type:GameDialogType.SELECT,
            title:'属性面板',
            content:`HP:${this.data.hp}\nMoney:${this.money}\nPos:(${this.data.position.x.toFixed(2)},${this.data.position.y.toFixed(2)},${this.data.position.z.toFixed(2)})`,
            options:['打开商店','打开背包']
        })

        if(xz)
        {
            if(xz.value == '打开商店')
            {
                this.shop()
            }
            else
            {
                this.openbag()
            }
        }
    }
    async shop()
    {
        const entity = this.data.entitys.player
        const list:string[] = []
        for(let i = 0;i < gunData.length;i++)
        {
            const e = gunData[i]
            list.push(`[${e.name}]-${e.money}$ ${this.bag.includes(i)?'[已拥有]':(e.money <= this.money?'':`（还需要${this.money-e.money}$）`)}`)
        }
        
        list.push('exit')
        while(true)
        {
            const xz = await entity.player.dialog<GameSelectDialogParams>({
                type:GameDialogType.SELECT,
                title:'商店',
                content:`你有${this.money}$`,
                options:list
            })

            if(xz && xz.value != 'exit')
            {
                const xz2 = await entity.player.dialog<GameSelectDialogParams>({
                    type:GameDialogType.SELECT,
                    title:'商店',
                    content:`名字:${gunData[xz.index].name}
类型：${gunData[xz.index].type}
射程：${gunData[xz.index].len}
射速：${10/gunData[xz.index].wait_time}发/秒
弹药：${gunData[xz.index].bullets}发
伤害：${gunData[xz.index].hurt}
价格:${gunData[xz.index].money}$`,
                    options:['购买','exit']
                })
                if(xz2 && xz2.value == '购买' && !this.bag.includes(xz.index))
                {
                    if(gunData[xz.index].money <= this.money)
                    {
                        this.bag.push(xz.index)
                        this.money -= gunData[xz.index].money
                        entity.player.directMessage(`你购买了${gunData[xz.index].name}！`)
                    }
                    else
                    {
                        entity.player.directMessage(`你没有足够的钱购买${gunData[xz.index].name}！`)
                    }
                }
            }
            else
            {
                break
            }
        }
    }

    async r_gun()
    {
        this.data.gun.Bullet = 0
        this.data.gun.can_fire = false

        this.data.entitys.player.player.directMessage('换弹中……')
        await sleep(5000)
        this.data.entitys.player.player.directMessage('换弹完成！')
        this.data.gun.Bullet = gunData[this.data.gun.gunId].bullets

        remoteChannel.sendClientEvent<Message<'gun'>>(this.data.entitys.player,{
            type:'gun',
            data:{
                text:`${this.data.gun.Bullet}/${gunData[this.data.gun.gunId].bullets}`,
                image:gunData[this.data.gun.gunId].image
            }
        })
  
        this.data.gun.can_fire = true
    }
}

export function find_player<T extends string|GameBounds3>(type:T extends string?'name':'Gamebound3',value:T):(T extends string?Player|undefined:Player[])
{
    if(type == 'name')
    {
        return players.find(e => e.data.name == value) as (T extends string?Player|undefined:Player[])
    }
    else if(type == 'Gamebound3')
    {
        const players2:Player[] = []
        for(const e of players)
        {
            if((value as GameBounds3).intersects(
                new GameBounds3(
                    e.data.entitys.bound.position.sub(e.data.entitys.bound.bounds),
                    e.data.entitys.bound.position.add(e.data.entitys.bound.bounds)
                )
            ) && e.data.hp >= 0)
            {
                players2.push(e)
            }
        }
        return players2 as (T extends string?Player|undefined:Player[])
    }
    return undefined as any
}


const speed = 0.5 

world.onTick(({tick}) => {
    for(const e of players)
    {
        if(e.data.hp <= 0)
            continue
        //
        e.data.position = e.data.entitys.bound.position.clone()

        const look_to = -Math.PI/2+Math.atan2(e.data.entitys.player.player.facingDirection.z,e.data.entitys.player.player.facingDirection.x)
        const look = new GameQuaternion(0,0,0,1).rotateY(e.data.entitys.player.player.cameraYaw+Math.PI/2)
        //
    
        if(e.data.entitys.player.player.walkState != GamePlayerWalkState.NONE)
        {
            const speed2 = e.data.entitys.player.player.walkState == GamePlayerWalkState.CROUCH || e.data.entitys.player.player.action1Button?speed*0.3:speed
            e.data.entitys.bound.velocity.x = e.data.entitys.player.player.facingDirection.x*speed2
            e.data.entitys.bound.velocity.z = e.data.entitys.player.player.facingDirection.z*speed2

            if(tick%4 == 0)
            {
                e.data.entitys.bound.sound('audio/step.mp3')
            }
            if(e.data.sport == 'None')
            {
                e.data.entitys.body.motion.setDefaultMotionByName('走路-射击')
                e.data.sport = 'Run'
            }
        }
        else
        {
            e.data.entitys.bound.velocity.x = 0
            e.data.entitys.bound.velocity.z = 0
            
            e.data.entitys.body.motion.setDefaultMotionByName('站立-射击')

            if(e.data.sport == 'Run')
            {
                e.data.entitys.body.motion.setDefaultMotionByName('走路-射击')
                e.data.sport = 'None'
            }
        }

        e.data.velocity = e.data.entitys.bound.position.sub(e.data.last_pos)

        e.data.last_pos = e.data.entitys.bound.position.clone()

        e.data.entitys.body.velocity = e.data.velocity
        e.data.entitys.body.position = e.data.position
        e.data.entitys.body.meshOrientation = look

        e.data.entitys.crama.velocity = e.data.velocity
        e.data.entitys.crama.position = e.data.position.add(entity_add_pos.body.crama)
        e.data.entitys.crama.meshOrientation = look

        e.data.entitys.gun.velocity = e.data.velocity

        if(e.data.entitys.player.player.action1Button)
        {
            const cos_pitch = 1//Math.cos(e.data.entitys.player.player.cameraPitch)
            const dir = new GameVector3(
                -Math.cos(e.data.entitys.player.player.cameraYaw)*cos_pitch,
                e.data.gun.wait_time/50-Math.sin(e.data.entitys.player.player.cameraPitch),
                -Math.sin(e.data.entitys.player.player.cameraYaw)*cos_pitch
            )
            
            e.data.entitys.gun.position = e.data.position
            e.data.entitys.gun.meshOffset = new GameVector3(0,0.4,0.5).scale(16)
            
            e.data.entitys.gun.lookAt(
                e.data.position.add(
                    dir.scale(1000)
                ),
            )

            if(e.data.gun.wait_time != 0)
                e.data.gun.wait_time--
            else
                e.data.gun.wait_time = 0

            e.data.entitys.player.player.cameraFovY = gunData[e.data.gun.gunId].fovy
        }
        else
        {
            e.data.entitys.player.player.cameraFovY = 0.5
            e.data.entitys.gun.position = e.data.position
            e.data.entitys.gun.meshOffset = new GameVector3(-0.1,0.3,0.6).scale(16)

            const cos_pitch = 1//Math.cos(e.data.entitys.player.player.cameraPitch)

            e.data.entitys.gun.lookAt(
                e.data.position.add(
                    new GameVector3(
                        -Math.cos(e.data.entitys.player.player.cameraYaw)*cos_pitch,
                        e.data.gun.wait_time/50-Math.sin(e.data.entitys.player.player.cameraPitch),
                        -Math.sin(e.data.entitys.player.player.cameraYaw)*cos_pitch
                    ).scale(1000)
                )
            )
            if(e.data.gun.wait_time != 0)
                e.data.gun.wait_time--
            else
                e.data.gun.wait_time = 0
        }
    
        
        if(tick%100 == 0)
        {
            e.data.hp = Math.min(max_hp,e.data.hp+10)
            e.data.entitys.bound.hp = e.data.hp
            remoteChannel.sendClientEvent<Message<'HP'>>(e.data.entitys.player,{
                type:'HP',
                data:e.data.hp
            })
        }
    }
})

world.onPlayerLeave(({entity}) => {
    const index = save_UserID.findIndex(e => e.userId == entity.player.userId)
    if(index != -1)
    {
        stor.set(entity.player.userId,{
            bag:save_UserID[index].player.bag,
            money:save_UserID[index].player.money
        })
        save_UserID.splice(index,1)
    }
})